/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.binding.internal.mapping;

import java.util.*;
import javax.swing.*;
import org.jdesktop.beansbinding.*;
import ch.trackedbean.binding.mapping.*;
import ch.trackedbean.loaders.*;

/**
 * {@link ISubBinding} for the list management of an components holding an element from a model and an external list (like {@link JComboBox}).<br>
 * The list content is loaded using {@link DataManager}.<br>
 * Just set this object via {@link BindingInfos#setSubBinding(ISubBinding)} on the corresponding binding.
 * 
 * @author M. Hautle
 */
public class ListBindingInfos implements ISubBinding, IDataListener {
    /** The binding to the destination component. */
    protected final Binding binding;

    /** The content type of the list. This information is used for the load of the content list. */
    protected final Class type;

    /**
     * Additional string specifying the "subtype" to load (may be null). This information is used for the load of the content list.
     */
    protected Object subType;

    /** Additional settings for the load (may be null). This information is used for the load of the content list. */
    protected Map settings;

    /** The converter to use for the list entries or null */
    protected Converter listConverter;

    /**
     * Default constructor
     * 
     * @param binding The binding to control
     * @param type The content type of the list
     * @param subType Additional discriminator specifying the "subtype" to load (may be null)
     * @param settings Additional settings for the load (may be null)
     */
    public ListBindingInfos(Binding binding, Class<?> type, Object subType, Map settings) {
        this.binding = binding;
        this.type = type;
        this.subType = subType;
        this.settings = settings;
    }

    /**
     * Returns the content type of the list.<br>
     * This property is used to call {@link DataManager#getList(Class, String, Map)}.
     * 
     * @return Returns the type.
     */
    public Class<?> getType() {
        return type;
    }

    /**
     * Returns an additional string specifying the "subtype" of the list (may be null).<br>
     * This property is used to call {@link DataManager#getList(Class, String, Map)}.
     * 
     * @return Returns the subType.
     */
    public Object getSubType() {
        return subType;
    }

    /**
     * Sets an additional string specifying the "subtype" of the list (may be null).<br>
     * This property is used to call {@link DataManager#getList(Class, String, Map)}.
     * 
     * @param subType The subType to set, may be <code>null</code>
     */
    public void setSubType(Object subType) {
        this.subType = subType;
    }

    /**
     * Returns the additional settings for the list (may be null).<br>
     * This property is used to call {@link DataManager#getList(Class, String, Map)}.
     * 
     * @return Returns the settings.
     */
    public Map getSettings() {
        return settings;
    }

    /**
     * Sets the additional settings for the list (may be null).<br>
     * This property is used to call {@link DataManager#getList(Class, String, Map)}.
     * 
     * @param settings The settings to set, may be <code>null</code>
     */
    public void setSettings(Map settings) {
        this.settings = settings;
    }

    /**
     * Returns the converter used for the source list of the component.
     * 
     * @return Returns the listConverter.
     */
    public Converter getListConverter() {
        return listConverter;
    }

    /**
     * Sets the converter used for the source list of the component.
     * 
     * @param listConverter The listConverter to set, may be <code>null</code>
     */
    public void setListConverter(Converter listConverter) {
        this.listConverter = listConverter;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public void bind(BindingInfos info) {
        binding.setConverter(listConverter);
        // set the content list
        triggerLoad();
        DataManager.addStructureDataListener(this, type);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("unchecked")
    public void unbind(BindingInfos info) {
        DataManager.removeStructureDataListener(this, type);
        if (binding.isBound())
            binding.unbind();
        binding.setSourceObject(null);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, D, K, V> void complexChange(Class<T> type, D subType, Map<K, V> settings) {
        triggerLoad();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, K> void changed(Class<T> type, K key) {
        triggerLoad();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, K> void created(Class<T> type, K key) {
        triggerLoad();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public <T, K> void removed(Class<T> type, K key) {
        triggerLoad();
    }

    /**
     * Triggers the list load.
     */
    @SuppressWarnings("unchecked")
    private void triggerLoad() {
        if (binding.isBound())
            binding.unbind();
        // TODO async load
        binding.setSourceObject(DataManager.getList(type, subType, settings));
        binding.bind();
    }
}
